/*
 * AudioControls.java
 *
 * Copyright (c) 2010 Jaymie Strecker (strecker at cs.umd.edu). All rights reserved.
 *
 * This file is part of Tonic.
 *
 * Tonic is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Tonic is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Tonic.  If not, see <http://www.gnu.org/licenses/>.
 */

package tonic.gui;

import javax.swing.*;
import java.io.*;
import tonic.audio.Player;
import java.awt.event.*;
import java.awt.FlowLayout;

/**
 * AudioControls is a panel that provides controls to open and play audio files. It has
 * two modes: no file open and file open. When a file is open, information about the file
 * is displayed and the play button is enabled. The user can toggle the play button to
 * play and pause the audio. The audio also pauses when it plays to the end of the file.
 *
 * AudioControls tells the Player to play and pause an audio file. It tells the
 * WaveDisplay to update itself when a file is opened and refresh itself while a file
 * is playing.
 *
 * @author Jaymie Strecker (strecker at cs.umd.edu)
 */
public class AudioControls extends JPanel {

    private WaveDisplay waveDisplay;
    private ChordDisplay chordDisplay;
    private JButton open;
    private JButton save;
    private JTextArea fileInfo;
    private JToggleButton play;
    private JTextArea markInstructions;
    private static final String MARK_INSTRUCTIONS_TEXT = "Hold down any key to mark an excerpt.";
    private ImageIcon openIcon, saveIcon, playIcon, pauseIcon;

    /**
     * Constructs an AudioControls with no file open.
     *
     * @param waveDisplay   the WaveDisplay to display any opened audio files
     */
    public AudioControls(WaveDisplay waveDisplay, ChordDisplay chordDisplay) {
        super();
        this.waveDisplay = waveDisplay;
        this.chordDisplay = chordDisplay;

        openIcon = createImageIcon("resources/Gnome-document-open.png", "Open");
        saveIcon = createImageIcon("resources/Gnome-document-save.png", "Save"); 
        playIcon = createImageIcon("resources/Fairytale_player_play.png", "Play");
        pauseIcon = createImageIcon("resources/Fairytale_player_pause.png", "Pause"); 

        open = new JButton(openIcon);
        open.setToolTipText("Open");
        save = new JButton(saveIcon);
        save.setToolTipText("Save");
        save.setEnabled(false); 
        JLabel fileInfoLabel = new JLabel("<html>File:<br>Duration:<br>Sample rate:</html>");
        fileInfo = new JTextArea(3, 30);
        fileInfo.setEditable(false);
        fileInfo.setBackground(getBackground());
        play = new JToggleButton(playIcon);
        play.setToolTipText("Play");
        play.setEnabled(false);
        markInstructions = new JTextArea(3, 30);
        markInstructions.setEditable(false);
        markInstructions.setLineWrap(true);
        markInstructions.setWrapStyleWord(true); 
        markInstructions.setText("");
        markInstructions.setBackground(getBackground()); 

        setLayout(new FlowLayout(FlowLayout.LEFT));
        add(open);
        add(save); 
        add(fileInfoLabel);
        add(fileInfo); 
        add(play);
        add(markInstructions);

        open.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                openFile();
            }
        });
        save.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                saveFile();
            }
        }); 
        play.addItemListener(new ItemListener() {
            public void itemStateChanged(ItemEvent e) {
                togglePlay(e);
            }
        });
    }

    /**
     * Event handler for opening a file. 
     */
    private void openFile() {
        File openedFile = DocumentHandler.open(this);
        if (openedFile != null) {
            fileInfo.setText(openedFile.getName()
                    + System.getProperty("line.separator")
                    + (Player.getNumSamples() / Player.getSamplesPerSecond()) + " sec."
                    + System.getProperty("line.separator")
                    + Player.getSamplesPerSecond() + " samples/sec.");
            waveDisplay.resetPlayer();
            chordDisplay.clear();
            play.setEnabled(true);
            save.setEnabled(true); 
        }
    }

    /**
     * Event handler for saving a file.
     */
    private void saveFile() {
        DocumentHandler.save(this); 
    }

    /**
     * Event handler for playing and pausing. Causes the Player and WaveDisplay to start
     * or pause playing, according to the given event.
     *
     * If the Player has a problem pausing, displays an error dialog.
     *
     * @param evt   the event sent from the play toggle button (ItemEvent.SELECTED if
     *              the button is being selected)
     */
    private void togglePlay(ItemEvent evt) {
        if (evt.getStateChange() == ItemEvent.SELECTED) {
            Player.play();
            waveDisplay.play();
            new PlayStopListener().start();
            play.setIcon(pauseIcon);
            play.setToolTipText("Pause");
            markInstructions.setText(MARK_INSTRUCTIONS_TEXT);
        } else {
            try {
                Player.pause();
            } catch (Exception e) {
                new ErrorDisplay(this, "There was a problem pausing the audio file.", e);
            }
            waveDisplay.pause();
            play.setSelected(false);
            play.setIcon(playIcon);
            play.setToolTipText("Play");
            markInstructions.setText("");
        }
    }

    /**
     * "Listener" to tell when the Player has stopped playing on its own because it reached
     * the end of the file.
     *
     * Ideally, this would be a LineListener listening for STOP events from the Player.
     * But the STOP event is sometimes not being fired by SourceDataLine, so this is a
     * work-around.
     */
    private class PlayStopListener extends Thread {
        // TODO Make this a singleton? 

        /**
         * Waits for the Player to finish playing. When it does, causes the WaveDisplay
         * to pause. 
         */
        @Override
        public void run() {
            try {
                Player.getPlayThread().join();
            } catch (InterruptedException e) {
                // nop
            }
            waveDisplay.pause();
            play.setSelected(false);
        }
    }

    /**
     * Returns an ImageIcon, or null if the path was invalid.
     * 
     * http://java.sun.com/docs/books/tutorial/uiswing/components/icon.html
     */
    protected ImageIcon createImageIcon(String path, String description) {
        java.net.URL imgURL = getClass().getResource(path);
        if (imgURL != null) {
            return new ImageIcon(imgURL, description);
        } else {
            return null;
        }
    }


}
